package goldgame;
import java.util.*;
import java.awt.*;
import java.awt.event.*;

/**
   Sample game for illustration. Intentionally stupid; more interesting
   games to be provided by students.
<p>
   Initially 20 gold coins are randomly placed in the matrix. The red gold
   collector aims to collect these coins which disappear after collection. 
   Each coin is randomly moved to a new position every n moves, where
   n is the number of remaining coins. Game is won when all coins collected
   and lost when collector leaves game board.
*/
public class GoldModel extends GameModel {
 
	private static final int EAST  = 0;
	private static final int WEST  = 1;
	private static final int NORTH = 2;
	private static final int SOUTH = 3;

	private static final int coinStartAmount = 20;

	/*
	  The following GameTile objects are used only
	  to describe how to paint the specified item.
	  
	  This means that they should only be used in
	  conjunction with the get/setGameboardState()
	  methods.
	 */

	/** Graphical representation of a coin. */
	private GameTile coinTile      = new RoundTile(new Color(255,215,0));

	/** Graphical representation of the collector */
	private GameTile collectorTile = new RoundTile(Color.red);

	/** Graphical representation of a blank tile. */
	private GameTile blankTile     = new GameTile();

	/** A list containing the positions of all coins. */
	private java.util.List<Position> coins = new ArrayList<Position>();
	/* The declaration and object creation above uses the new language feature
	 * 'generic types'. It can be declared in the old way like this:
	 *    	private java.util.List coins = new ArrayList();
	 * This will however result in a warning at compilation
	 * "Position" in this case is the type of the objects that are going 
	 * to be used in the List
	 */


	/** The position of the collector. */
	private Position collectorPos;

	/** The direction of the collector. */
	private int direction;

	/** The number of coins found. */
	private int score;


	/**
	 * Create a new model for the gold game.
	 */
	public GoldModel() {
		Dimension size = getGameboardSize();

		// Blank out the whole gameboard...
		for (int i=0; i<size.width; i++) {
			for (int j=0; j<size.height; j++) {
				setGameboardState(i, j, blankTile);
			}
		}
		
		// Insert the collector in the middle of the gameboard.
		collectorPos = new Position(size.width/2, size.height/2);
		setGameboardState(collectorPos, collectorTile);

		// Insert coins into the gameboard.
		for (int i=1; i<coinStartAmount; i++) {
			addCoin();
		}
	}
    
	/**
	 * Insert another coin into the gameboard.
	 */
	private void addCoin() {
		// Make sure that two coins aren't placed in the same position
		Position newCoinPos;
		Dimension size = getGameboardSize();
		do {
			newCoinPos = new Position((int)(Math.random() * size.width),
									  (int)(Math.random() * size.height));
		} while (!isPositionEmpty(newCoinPos));
		
		// Actually add the new coin
		setGameboardState(newCoinPos, coinTile);
		coins.add(newCoinPos);
	}

	/**
	 * Return whether the specified position is empty.
	 *
	 * @param pos The position to test.
	 * @return true if position is empty.
	 */
	private boolean isPositionEmpty(Position pos) {
		return (getGameboardState(pos) == blankTile);
	}
	

	/**
	 * Update the direction of the collector
	 * according to the users keypress.
	 */
	private void updateDirection(int key) {
		switch (key) {
		case KeyEvent.VK_LEFT:  
			direction = WEST;  
			break;
		case KeyEvent.VK_UP:  
			direction = NORTH;
			break;
		case KeyEvent.VK_RIGHT:
			direction = EAST;
			break;
		case KeyEvent.VK_DOWN:
			direction = SOUTH;
			break;
		default:
			// Don't change direction if another key is pressed
			break;
		}
	}

	/**
	 * Get next position of the collector.
	 */
	private Position getNextCollectorPos() {
		Position nextPos = null;
		switch (direction) {
		case EAST: 
			nextPos = new Position(collectorPos.x+1,collectorPos.y);
			break;
		case NORTH: 
			nextPos = new Position(collectorPos.x,collectorPos.y-1);
			break;
		case WEST: 
			nextPos = new Position(collectorPos.x-1,collectorPos.y);
			break;
		case SOUTH: 
			nextPos = new Position(collectorPos.x,collectorPos.y+1);
			break;
		}
		return nextPos;
	}
	
        /**
	 * This method is called repeatedly so that the
	 * game can update it's state.
	 *
	 * @param lastKey The most recent keystroke.
	 */
	public void gameUpdate(int lastKey) throws GameOverException {
		try {
			updateDirection(lastKey);

			// Change collector position
			setGameboardState(collectorPos, blankTile);
			collectorPos = getNextCollectorPos();
			setGameboardState(collectorPos, collectorTile);

			// Remove the coin at the new collector position (if any)
			boolean found = coins.remove(collectorPos);
			if (found) {
				score++;
			}

			// Check if all coins are found
			if(coins.isEmpty())
			    throw new GameOverException(score+5);

			// Remove one of the coins
			Position oldCoinPos = (Position)coins.get(0);
			coins.remove(0);
			setGameboardState(oldCoinPos, blankTile);

			// Add a new coin (simulating moving one coin)
			addCoin();

		} catch (ArrayIndexOutOfBoundsException e) {
			// When an ArrayIndexOutOfBoundsException occurs, this
			// means that the collector will try to move outside
			// the valid gameboard, and then the game is over.
			
			throw new GameOverException(score);
		}
	}
}
